/***************************************************************************
*  Mainwidget.cpp
*
*  July 2007
*  Copyright  2007  RuijinZheng,China
*  Email mebetop@126.com
****************************************************************************/

/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
#include <QMessageBox>
#include <QMouseEvent>
#include <QDragEnterEvent>
#include <QDropEvent>
#include <QPoint>
#include <QPixmap>
#include <QBitmap>
#include <QPalette>
#include <QStringList>
#include <QList>
#include <QListWidgetItem>
#include <QFileDialog>
#include <QIODevice>
#include <QTextStream>
#include <QUrl>
#include "Mainwidget.h"
#include "Aboutme.h"
using namespace Qt;
MainWidget *mainpointer;
void handleSubProcessExitEvent(int sig_no){
    pthread_t waitSub;
    int ret = pthread_create(&waitSub, NULL, (void*(*)(void*))(MainWidget::waitForSubProcessExit), (void*)mainpointer);
    if(ret){
    	mainpointer->showError("error occured while starting sub-thread to wait for sub-process");
    }
}
void itoa(int i, char* string){
    int power, j ;
    j=i;
    for(power=1; j >= 10; j/=10)
        power*=10;
    for(; power > 0 ; power/=10)
    {
        *string++ = '0'+i/power;
        i%=power;
    }
    *string = '\0';
}
MainWidget::MainWidget(QWidget *parent,Qt::WindowFlags f): QDialog(parent,FramelessWindowHint|WindowStaysOnTopHint){
    setupUi(this); 
    qRegisterMetaType<pid_t>("pid_t");
    mainpointer=this;
    setAcceptDrops(true);
    enabledToWork=0;
    this->setGeometry(800,0,900,50);
    pthread_mutex_init(&mut,NULL);
    this->curWidget=NULL;
    this->menuCheckedIndex=-1;
    this->config=new ConfigureBase();
    this->resize(61,61);
    this->pushButtonContrl->setIcon(QIcon(":/icons/logo.png"));
    this->pushButtonContrl->setIconSize(QSize(61,61));
    this->toolButtonExit->setIcon(QIcon(":/icons/exit.png"));
    this->toolButtonExit->setIconSize(QSize(41,41));
    this->toolButtonAboutMe->setIcon(QIcon(":/icons/me.png"));
    this->toolButtonAboutMe->setIconSize(QSize(41,41));
    this->toolButtonHistory->setIcon(QIcon(":/icons/history.png"));
    this->toolButtonHistory->setIconSize(QSize(41,41));
    this->toolButtonProxyClients->setIcon(QIcon(":/icons/see.png"));
    this->toolButtonProxyClients->setIconSize(QSize(41,41));
    this->toolButtonProxyServers->setIcon(QIcon(":/icons/list.png"));
    this->toolButtonProxyServers->setIconSize(QSize(41,41));
    this->toolButtonEditSettings->setIcon(QIcon(":/icons/config.png"));
    this->toolButtonEditSettings->setIconSize(QSize(41,41));
    this->toolButtonAdd->setIcon(QIcon(":/icons/add.png"));
    ////////////////
    this->toolButtonGuid->setIconSize(QSize(41,41));
    this->toolButtonGuid->setIcon(QIcon(":/icons/guide.png"));
    ////////////////
    this->toolButtonAdd->setIconSize(QSize(41,41));
    this->origwidth=61;
    this->origHeight=61;
    this->menuHeight=61;
    this->compleWidth=this->groupBoxOperates->width()+2;
    this->compleHeight=120+this->groupBoxOperates->height();
    connect(pushButtonContrl,SIGNAL(toggled(bool)),this,SLOT(setMenuShown(bool)));
    connect(toolButtonProxyClients,SIGNAL(toggled(bool)),this,SLOT(manageClient(bool)));
    connect(toolButtonEditSettings,SIGNAL(toggled(bool)),this,SLOT(manageSettings(bool)));
    connect(toolButtonProxyServers,SIGNAL(toggled(bool)),this,SLOT(manageProxyServers(bool)));
    connect(toolButtonHistory,SIGNAL(toggled(bool)),this,SLOT(manageHistory(bool)));
    connect(toolButtonAboutMe,SIGNAL(clicked()),this,SLOT(showAboutMe()));
    connect(this,SIGNAL(setCheckedStatus()),this,SLOT(changeCheckedStatus()));
    /////////////////////////////
    connect(this,SIGNAL(analyseThisTarget(QString)),this,SLOT(analyseTarget(QString)));
    connect(this,SIGNAL(startClientProcess(QString,QString)),this,SLOT(processHandler(QString,QString)));
    connect(this,SIGNAL(doThisPreparation(int)),this,SLOT(exeThisPreparation(int)));
    ///////////////
    connect(this,SIGNAL(readyToAddNewClient(QString,QString,pid_t)),this,SLOT(addClientToList(QString,QString,pid_t)));
    ///////////////////
    connect(toolButtonExit,SIGNAL(clicked()),this,SLOT(exitNow()));
    ///////////////////
    connect(toolButtonAdd,SIGNAL(clicked()),this,SLOT(subButtonClicked()));
    ////////////////////
    connect(this,SIGNAL(addHistory(QString)),this,SLOT(addToHistory(QString)));
    bool conf;
    if((conf=this->loadConfig())==false){
		switch( QMessageBox::warning( this, "Warning",
		        "Could not load Global settings.\n"
		        "This program can't function correctly "
		        "without the settings.\n\n",
		        "Configure the settings now",
		        "Abort it right now", 0, 0, 1 ) ){
		    case 0: 
		    	  emit doThisPreparation(1);
		        break;
		    case 1: 
		    	enabledToWork=1;
		        break;
		}
    }
    if(this->loadProxyServer()==false){
			switch( QMessageBox::warning( this, "Warning",
			        "Could not load proxy server list .\n"
			        "This program can't function correctly "
			        "without  proxy server.\n\n",
			        "Configure it now",
			        "Abort it right now", 0, 0, 1 ) ){
			    case 0: 
			    	  emit doThisPreparation(0);
			        break;
			    case 1: 
			    	  enabledToWork=(enabledToWork==1)?3:2;
			        break;
			}
   }
   struct sigaction        SigChldAction;
   sigset_t                SigChldSet;
   if(sigemptyset(&SigChldSet)){
       QMessageBox::warning(this, tr("Error"),
            tr("Error occured while calling sigemptyset()."));
    }
   if(sigaddset(&SigChldSet, SIGCHLD)) {
       QMessageBox::warning(this, tr("Error"),
            tr("Error occured while calling sigaddset()."));
   }
   SigChldAction.sa_handler = handleSubProcessExitEvent;
   SigChldAction.sa_flags = 0;
   sigaction(SIGCHLD, &SigChldAction, NULL);
   
} 
MainWidget::~MainWidget(){
	if(this->config!=NULL){
		delete this->config;
	}
	AbstractClientElement *tmp;
	QList<AbstractClientElement *>::iterator it=clientlist.begin();
	while(it!=clientlist.end()){
		tmp = *it;
		delete tmp;
		it++;
	}
}
void MainWidget::mouseMoveEvent(QMouseEvent *e){
     if (!(e->buttons() & Qt::LeftButton))
         return;
     QPoint newpos = e->globalPos();
     QPoint upleft = pos0 + newpos - last;
     move(upleft);
}
void MainWidget::mousePressEvent(QMouseEvent *e){
     if (e->button() == Qt::LeftButton)
     last = e->globalPos();
     pos0 = e->globalPos() - e->pos();
     
}
void MainWidget::dragEnterEvent(QDragEnterEvent *event){
     event->accept();
}
void MainWidget::dropEvent(QDropEvent *event){
     QString fileName,Exec;
     QList<QUrl> urllist = event->mimeData()->urls();
     fileName=urllist[0].toLocalFile();

     emit analyseThisTarget(fileName);
} 
void MainWidget::setMenuShown(bool shown){
	if(shown==true){
		this->resize(compleWidth,menuHeight);
	}
	else{
		emit setCheckedStatus();
		this->resize(origwidth,origHeight);
	}
	this->groupBoxMenu->setShown(shown);
}
void  MainWidget::manageClient(bool shown){

	if(shown==true){
		if(this->menuCheckedIndex!=-1&&this->menuCheckedIndex!=2){
			emit setCheckedStatus();
		}
		this->resize(compleWidth,compleHeight);
		this->labelMenu->setText(tr("View process "));
		ClientView *view = new ClientView();
		this->vboxLayout->addWidget(view);
		view->resize(this->compleWidth,this->groupBoxOperates->height());
		QList<AbstractClientElement *>::iterator it=clientlist.begin();
		while(it!=clientlist.end()){
			view->addClientToWidget(**it);
			it++;
		}
		curWidget=view;
		this->menuCheckedIndex=2;
	}
	else{
		this->resize(compleWidth,menuHeight);
		this->vboxLayout->removeWidget(curWidget);
		delete curWidget;
		this->menuCheckedIndex=-1;
	}
	this->groupBoxOperates->setShown(shown);

}
void  MainWidget::manageSettings(bool shown){
	if(shown==true){
		if(this->menuCheckedIndex!=-1&&this->menuCheckedIndex!=0){
			emit setCheckedStatus();
		}
		this->resize(compleWidth,compleHeight);
		this->labelMenu->setText(tr("Configure settings"));
		ConfigGlobal *con = new ConfigGlobal(0,0,this->config);
		connect(con,SIGNAL(settingsHaveBeenChanged()),this,SLOT(loadConfig()));
		
		connect(con,SIGNAL(rollUp()),this,SLOT(changeCheckedStatus()));
		this->vboxLayout->addWidget(con);
		con->resize(this->compleWidth,this->groupBoxOperates->height());
		curWidget=con;
		this->menuCheckedIndex=0;
	}
	else{
		this->resize(compleWidth,menuHeight);
		this->vboxLayout->removeWidget(curWidget);
		delete curWidget;
		this->menuCheckedIndex=-1;
	}
	this->groupBoxOperates->setShown(shown);

}
void  MainWidget::manageProxyServers(bool shown){
	if(shown==true){
		if(this->menuCheckedIndex!=-1&&this->menuCheckedIndex!=1){
			emit setCheckedStatus();
		}
		this->resize(compleWidth,compleHeight);
		this->labelMenu->setText(tr("Edit proxy servers list"));
		Editproxylist *list = new Editproxylist();
		connect(list,SIGNAL(proxyListChanged()),this,SLOT(loadProxyServer()));
		connect(list,SIGNAL(rollUp()),this,SLOT(changeCheckedStatus()));
		this->vboxLayout->addWidget(list);
		list->resize(this->compleWidth,this->groupBoxOperates->height());
		curWidget=list;
		this->menuCheckedIndex=1;
	}
	else{
		this->resize(compleWidth,menuHeight);
		this->vboxLayout->removeWidget(curWidget);
		delete curWidget;
		this->menuCheckedIndex=-1;
	}
	this->groupBoxOperates->setShown(shown);

}
void  MainWidget::manageHistory(bool shown){
	if(shown==true){
		if(this->menuCheckedIndex!=-1&&this->menuCheckedIndex!=3){
			emit setCheckedStatus();
		}
		this->resize(compleWidth,compleHeight);
		this->labelMenu->setText(tr("Manage history records"));
		EditHistory *his = new EditHistory();
		connect(his,SIGNAL(runHistory(QString)),this,SLOT(analyseTarget(QString)));
		connect(his,SIGNAL(rollUp()),this,SLOT(changeCheckedStatus()));
		this->vboxLayout->addWidget(his);
		his->resize(this->compleWidth,this->groupBoxOperates->height());
		curWidget=his;
		this->menuCheckedIndex=3;
	}
	else{
		this->resize(compleWidth,menuHeight);
		this->vboxLayout->removeWidget(curWidget);
		delete curWidget;
		this->menuCheckedIndex=-1;
	}
	this->groupBoxOperates->setShown(shown);
}
void  MainWidget::showAboutMe(){
	AboutMe about;
	about.show();
	about.exec();
}
void  MainWidget::changeCheckedStatus(){
	switch(this->menuCheckedIndex){
	case 0:
		this->toolButtonEditSettings->setChecked(false);
		break;
	case 1:
		this->toolButtonProxyServers->setChecked(false);
		break;
	case 2:
		this->toolButtonProxyClients->setChecked(false);
		break;
	case 3:
		this->toolButtonHistory->setChecked(false);
		break;

	default:
		break;
		
	}
}
void MainWidget::addToHistory(QString path){
	 QString history=QString(getenv("HOME"))+QString("/.netflea/history.list");
    QFile file(history);
    if(file.open(QIODevice::ReadWrite|QIODevice::Append)){
        QTextStream in(&file);
        in<<path<<"\n";
        file.flush();  
        file.close();
   }
}
void MainWidget::analyseTarget(QString fileName){
	QString Exec;
	QString Icon;
	if(enabledToWork==0){
		if (!(fileName.isNull())){
			QFileInfo fi(fileName);
				if(!fi.exists()){
						QMessageBox::warning(this, tr("Error"),
								tr("File not exits!"));
				}else{
			        if(fi.isSymLink()){
			            Exec = fi.symLinkTarget();
			            Icon=QString(LINKTARGETIMAGE);
			            emit addToHistory(fileName);
			            emit startClientProcess(Exec,Icon);

			        }
			        else if(fi.isFile()){
			             if(fi.suffix()=="desktop"){
			            	    QFile file(fileName);
			            	    if (!file.open(QIODevice::ReadOnly)){
			            	        QMessageBox::warning(this, tr("Error"),
			            	              tr("Cannot read file %1:\n%2.")
			            	                    .arg(file.fileName())
			            	                    .arg(file.errorString()));
			            	     }
			            	    QTextStream in(&file);
			            	    QString tmpstring;
			            	    while(!(in.atEnd())){
			            	       tmpstring = in.readLine();
			            	       if(tmpstring.startsWith("Exec=")){
			            	           Exec=tmpstring.remove(0,5);
			            	        }
			            	       else if(tmpstring.startsWith("Icon=")){
			            	    	     Icon=tmpstring.remove(0,5);
			            	    	     QFileInfo iconinfo(Icon);
			            	    	     if(iconinfo.fileName()==iconinfo.filePath()){
			            	    	    	 Icon=QString(UNKOWNTARGETIMAGE);
			            	    	     } 
			            	       }   
			            	    }
			            	    file.close();
			            	    emit addToHistory(fileName);
			            	    emit startClientProcess(Exec,Icon);
			             }
			           else if(fi.isExecutable()){
			        	   	Exec=fileName;
			        	   	Icon=QString(EXECUTIVETARGETIMAGE);
			        	   	emit addToHistory(fileName);
			        	   	emit startClientProcess(Exec,Icon);
			             }
			           else{
			                   QMessageBox::warning(this, tr("Error"),
			                       tr("please drag a standar launcher , link or excutable file."));
			             }
			        }
				}

		}
        else{
             QMessageBox::warning(this, tr("Error"),
                  tr("File name is null."));
        } 
  }else{
	    QString errormsg;
	    switch(enabledToWork){
	    case 1:
	    	errormsg="global settings not configured,netflea not ready to work";
	    case 2:
	    	errormsg="no proxy server provided,netflea not ready to work";
	    case 3:
	    	errormsg="both 'global settings not configured' and 'no proxy servers provided'\n netflea not ready to work";
	    }
       QMessageBox::warning(this, tr("Error"),
            tr("%1")
            .arg(errormsg));
  }
}
bool MainWidget::processHandler(QString exec,QString icon){
    QStringList stringlist = exec.split(" ",
    QString::SkipEmptyParts);
    argtag = new char *[stringlist.count()+1+2];
    int i;
    for(i=0;i<stringlist.count();i++){
         argtag[2+i] = new char[stringlist[i].length()+1];
         strcpy(argtag[2+i],stringlist[i].toStdString().c_str());
    }
   
    argtag[0] = new char[this->config->getProxyType().length()+1];
    strcpy(argtag[0],this->config->getProxyType().c_str());
    
    argtag[1] = new char[icon.length()+1];
    strcpy(argtag[1],icon.toStdString().c_str());
    
    argtag[stringlist.count()+2]=NULL;
    pthread_t exethread;
    int ret = pthread_create(&exethread, NULL, (void*(*)(void*))(MainWidget::exeThread), (void*)this);
    if(ret){
         QMessageBox::critical(0, tr("Error"),tr("error occured while creating sub thread to execute program"));
    }
}
void MainWidget::exeThread(MainWidget *main){
    pthread_detach(pthread_self());
    int totallength=0,num=0,curpos=0;
    char **p=main->argtag;
    while(*p!=NULL){
        num++;
        totallength+=strlen(*p);
        p++;
    }
    char stackargs[totallength+num];
    char *newargs[num+1];
    for(int i=0;i<num;i++){
        newargs[i]=&stackargs[curpos];
        strcpy(&stackargs[curpos],main->argtag[i]);
        curpos+=strlen(main->argtag[i]);
        curpos++;
        delete [] main->argtag[i];
    }
    delete [] main->argtag;
    newargs[num]=NULL;
    pid_t pid;
    int status;
    pid=fork();
    switch(pid){
          case-1:
          {
               emit main->errorHappenedInSubThread(tr("Error occured while creating new process,\
                              ignoring... "));
               break;
          }
          case 0:
          {
              // std::string serverlist = std::string("PROXYSERVERS=")+std::string(newargs[0]);
               std::string proxytype = std::string("FLEATYPE=")+std::string(newargs[0]);
               std::string modeforshowerr=std::string("SHOWERRMODE=Ui");
               std::string programnme = std::string("PROGRAM_NAME=")+std::string(newargs[2]);
               char itoatmp[20];
               itoa(main->config->getConnectTimeOut(),itoatmp);
               std::string timeoutvalue = std::string("CONNECTTIMEOUT=")
                             +std::string(itoatmp);
               itoa(main->config->getUdpMaxPort(),itoatmp);
               std::string maxport = std::string("MAXPORT=")
                             +std::string(itoatmp);
               itoa(main->config->getUdpMinPort(),itoatmp);
               std::string minport = std::string("MINPORT=")
                             +std::string(itoatmp);
               putenv(LIBPATH);
             //  putenv((char *)serverlist.c_str());
               putenv((char *)proxytype.c_str());
               putenv((char *)modeforshowerr.c_str());
               putenv((char *)programnme.c_str());
               putenv((char *)timeoutvalue.c_str());
               putenv((char *)maxport.c_str());
               putenv((char *)minport.c_str());
               if(execvp(newargs[2], &(newargs[2]))==-1){
                    exit(1);
                  }
               break;
          }
          default:{
              QString name(newargs[2]);
              QString icon(newargs[1]);
              emit main->readyToAddNewClient(name,icon,pid);
              usleep(1000);
              pthread_exit(NULL);

          }
    } 
}
void MainWidget::waitForSubProcessExit(MainWidget *mainpointer){
    pthread_detach(pthread_self());
	pid_t   ChildPid;
	int ChildStatus;
	while((ChildPid = wait((int*)(&ChildStatus))) != -1) {
		mainpointer->handleSubProcessExit(ChildPid);
	}
    pthread_exit(NULL);
}
void MainWidget::addClientToList(QString clientname,QString image,pid_t pid){

	 AbstractClientElement *abs=new AbstractClientElement();
	 abs->setPid(pid);
	 abs->setLocation(clientname);
	 abs->setImagePath(image);
	 this->clientlist.push_back(abs);
	 if(menuCheckedIndex==2){
		 ((ClientView *)curWidget)->addClientToWidget(*abs);
	 }

}
bool MainWidget::loadProxyServer(){
     QString proxy(getenv("HOME"));
     proxy+="/.netflea/proxyserver.list";
     QFile proxyserverfile(proxy);
     proxylist="";
     if (!proxyserverfile.open(QIODevice::ReadOnly)){
         proxyserverfile.close();
         return false;
     }
     QTextStream in(&proxyserverfile);
     QString tmpstring;
     while(!(in.atEnd())){
          tmpstring = in.readLine();
          if(!(tmpstring.startsWith("#"))){
               proxylist+=(tmpstring+"@");
          }
     }
     proxyserverfile.close();
     if(proxylist.length()<10)
          return false;
     return true;
}
bool MainWidget::loadConfig(){
	this->config = new ConfigureBase();
	return this->config->readSettings();
}
void  MainWidget::exeThisPreparation(int code){
	if(code==1){
		 ConfigGlobal con(this,0,this->config,true);
   	 con.exec();
	}else if(code==0){
		Editproxylist edit(this,0,true);
	    edit.exec();
	}
}
void MainWidget::showError(QString error){
     QMessageBox::critical(0, tr("Error"),tr("%1")
                           .arg(error));
}
void MainWidget::handleSubProcessExit(pid_t pid){
	AbstractClientElement *tmp;
	int i;
	for(i=0;i<clientlist.count();i++){
		if(clientlist.at(i)->getPid()==pid){
			tmp=clientlist.takeAt(i);
			delete tmp;
		}
	}
	if(this->menuCheckedIndex==2){
		((ClientView *)curWidget)->delClientFromWidget(pid);
	}
}
void MainWidget::subButtonClicked()
{
     QString fileName;
     fileName = QFileDialog::getOpenFileName(this, tr("Browse to a program or launcher to start it"),
             "/home/", tr("All File (*)"));
      if(!fileName.isNull()){
    	   emit analyseThisTarget(fileName);
       }
}
void MainWidget::exitNow(){
	qApp->quit();
}